package com.fs.web.base.http;

import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *@desc http请求助手
 *@author TiddlerCJ
 */
public class HttpHelper {
	private static final HttpHelper helper = new HttpHelper();
	private final static Object syncLock = new Object();
	/*
	 * 创建httpclient连接池
	 */
	private PoolingHttpClientConnectionManager httpClientConnectionManager = null;
	private RequestConfig requestConfig = null ;
	private CloseableHttpClient httpClient = null ;
	/*
	 * 单例
	 */
	public static HttpHelper Instance() {
		return helper;
	}
	
	private HttpHelper(){ 
		init();
	}

	/*
	 * 初始化工作
	 */
	private void init() {
		ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
		LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
		Registry<ConnectionSocketFactory> registry = RegistryBuilder
				.<ConnectionSocketFactory> create()
				.register("http", plainsf)
				.register("https", sslsf).build();

		// 初始化连接池
		httpClientConnectionManager = new PoolingHttpClientConnectionManager(
				registry);
		httpClientConnectionManager
				.setMaxTotal(HttpConfig.MAX_TOTAL);
		httpClientConnectionManager
				.setDefaultMaxPerRoute(HttpConfig.MAX_PER_ROUTE);
		// 初始化全局的requestConfig
		requestConfig = RequestConfig.custom()
				.setConnectionRequestTimeout(HttpConfig.CONECTION_TIMEOUT)
				.setConnectTimeout(HttpConfig.CONECTION_TIMEOUT)
				.setSocketTimeout(HttpConfig.CONECTION_TIMEOUT)
				.build();
	}
	
	/*
	 * 获取httpClient对象
	 */
	public CloseableHttpClient getHttpClient() {
		if (httpClient == null) {
			synchronized (syncLock) {
				if (httpClient == null) {
					// 声明重定向策略对象及重试策略
					LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
					httpClient = HttpClients.custom()
							.setConnectionManager(httpClientConnectionManager)
							.setDefaultRequestConfig(requestConfig)
							.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
								public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) {
									 return 20 * 1000; // tomcat7默认keepAliveTimeout为20s
								}
							})
							.setRedirectStrategy(redirectStrategy)
							.setRetryHandler(myRetryHandler).build();
				}
			}
		}
		return httpClient;
	}
	/*
	 * 创建请求
	 */
	private HttpUriRequest createHttpRequest(String url , String method, String encodeType, Map<String, String> param){
		HttpUriRequest request = null;
		try {
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if(param!=null && param.size()>0){
				for(String key : param.keySet()){
					nvps.add(new BasicNameValuePair(key, param.get(key)));
				}
			}
			if(HttpConfig.METHOD_POST.equals(method)){
				request = RequestBuilder.post().setCharset(Charset.forName(encodeType)) .setUri(new URI(url))
						.setEntity(new UrlEncodedFormEntity(nvps, encodeType)).build();
			}else{
				String getParam = URLEncodedUtils.format(nvps, encodeType);
				if(url.indexOf("?")!=-1){
					url += ("&" + getParam);
				}else{
					url += ( "?"+ getParam );
				}
				request = RequestBuilder.get().setCharset(Charset.forName(encodeType)) .setUri(new URI(url)).build();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return request;
	}
	
	
	public String httpGet(String callUrl, Map<String, String> data) {
		return httpGet(callUrl, data, HttpConfig.UTF_8); //默认UTF-8
	}
	
	public String httpGet(String callUrl, Map<String, String> data,String encodeType) {
		CloseableHttpResponse response = null ;
		String responseEntity = null ; 
		HttpEntity httpEntity = null;
		try {
			HttpUriRequest request = createHttpRequest(callUrl, HttpConfig.METHOD_GET, encodeType, data);
			response = getHttpClient().execute(request);
			int statuCode = response.getStatusLine().getStatusCode();  
			httpEntity = response.getEntity(); 
			switch (statuCode){  
	            case 200:  
	            	if(httpEntity!=null){
	            		responseEntity = EntityUtils.toString(httpEntity, encodeType);
	            	}
	                break;  
	            default:
	            	System.err.println("statuCode: "+ statuCode + " url: "+ callUrl + " param: "+ data );  
	            	break;
            }  
		} catch (Exception e) {
			System.err.println("httpGet error :"+ e);
		} finally {
			if(null != response) {
				try { response.close(); } catch (IOException e) {}
			}
		}
		return responseEntity;
	}
	
	
	
	/**
	 * 发起请求
	 * @param callUrl
	 * @param data
	 * @return
	 */
	public String httpPost(String callUrl, Map<String, String> data) {
		return httpPost(callUrl, data, HttpConfig.UTF_8); //默认UTF-8
	}
	
	/**
	 * 发起请求
	 * @param callUrl
	 * @param data
	 * @param encodeType
	 * @return
	 */
	public String httpPost(String callUrl, Map<String, String> data,String encodeType) {
		CloseableHttpResponse response = null ;
		String responseEntity = null ; 
		HttpEntity httpEntity = null;
		try {
			HttpUriRequest request = createHttpRequest(callUrl, HttpConfig.METHOD_POST, encodeType, data);
			response = getHttpClient().execute(request);
			int statuCode = response.getStatusLine().getStatusCode();  
			httpEntity = response.getEntity(); 
			switch (statuCode){  
	            case 200:  
	            	if(httpEntity!=null){
	            		responseEntity = EntityUtils.toString(httpEntity, encodeType);
	            		System.out.println("responseEntity is: "+ responseEntity);
	            	}
	                break;  
	            default:
	            	System.err.println("statuCode: "+ statuCode + " url: "+ callUrl + " param: "+ data );  
	            	break;
            }  
		} catch (Exception e) {
			System.err.println("httpPost error :"+ e);
		} finally {
			if(null != response) {
				try { response.close(); } catch (IOException e) {}
			}
		}
		return responseEntity;
	}
	
	/**
	 * 发起请求（返回状态码）
	 * @param callUrl
	 * @param data
	 * @param encodeType
	 * @return
	 */
	public Map<String,String> httpPostCode(String callUrl, Map<String, String> data,String encodeType) {
		Map<String,String> map = new HashMap<String,String>();
		CloseableHttpResponse response = null ;
		String responseEntity = null ; 
		HttpEntity httpEntity = null;
		try {
			HttpUriRequest request = createHttpRequest(callUrl, HttpConfig.METHOD_POST, encodeType, data);
			response = getHttpClient().execute(request);
			int statuCode = response.getStatusLine().getStatusCode();  
			httpEntity = response.getEntity(); 
			switch (statuCode){  
			case 200:  
				if(httpEntity!=null){
					responseEntity = EntityUtils.toString(httpEntity, encodeType);
					System.out.println("responseEntity is: "+ responseEntity);
				}
				break;  
			default:
				System.err.println("statuCode: "+ statuCode + " url: "+ callUrl + " param: "+ data );  
				break;
			}
			map.put("responseEntity", responseEntity);
			map.put("statuCode", Integer.toString(statuCode));
		} catch (Exception e) {
			System.err.println("httpPost error :"+ e);
			map = null;
		} finally {
			if(null != response) {
				try { response.close(); } catch (IOException e) {}
			}
		}
		return map;
	}
	
	/*
	 *重新请求机制
	 */
	HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
		public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
			System.err.println("请求次数："+ executionCount);
			if (executionCount >= HttpConfig.RETRY_COUNT) { 			// 超过三次则不再重试请求
				return false;
			}
			if (exception instanceof org.apache.http.conn.HttpHostConnectException) {	// 如果服务器丢掉了连接，那么就重试
				return true;
			}
			if (exception instanceof NoHttpResponseException) {	// 如果服务器丢掉了连接，那么就重试
				return true;
			}
			if (exception instanceof org.apache.http.conn.ConnectTimeoutException) {	//连接超时  
				return true;  
			}  
			if (exception instanceof java.net.UnknownHostException) {//未找到主机  
				return true;  
			}  
			if (exception instanceof InterruptedIOException) {		//io操作中断,Timeout
		          return false;  
	        }  
			if (exception instanceof SSLException) {				// SSL handshake exception
				return false;
			}
			HttpClientContext clientContext = HttpClientContext.adapt(context);
			HttpRequest request = clientContext.getRequest();
			if (!(request instanceof HttpEntityEnclosingRequest)){	// Retry if the request is considered idempotent
				return true;
			}
			return false;
		}
	};
}
